#include <minix/drivers.h>
#include <minix/vtreefs.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "bencode.h"

#define FILE_LOCATION "/mnt/hgfs/minix_shared/as3/as3_t4/file.ben"

/** The struct which contains a rss channel */
typedef struct rsschannel {
	char *title;
	char *link;
	char *description;
	char *language;
	struct rssitem **items;
	int itemCount;
} rsschannel;

/** One item in a rss channel */
typedef struct rssitem {
	char* title;
	char* link;
	char* description;
	char* pubDate;
	char* guid;
} rssitem;

/** The rss channel read from the file */
static struct rsschannel *rss;

/** String used for storage when reading data from a file */
static char *data;

/** A list of all the inodes of the files. */
static struct inode **files;

/** 
 * Get a list of indeces of the positions of the lookup char and replace the lookup char by the replace char
 * @return a list indices of positions of the lookup char
 */
int* replaceChars(char *string, char lookup, char replace) {
	int i = -1;
    int *result;
	int resultSize = 0;
    
    // Find size of result array
    while (string[++i] != '\0') {
		if (string[i] == lookup) {
			resultSize++;
		}
	}
    // Alloc space
    result = malloc(resultSize*sizeof(int));   
    // Reset vars
    i=-1;    
    resultSize=0;
    // Set the indices
	while (string[++i] != '\0') {
		if (string[i] == lookup) {
            string[i] = replace;
			result[resultSize] = i;
            resultSize++;
		}
	}
    // Return the array
	return result;
}

/** Fetch the directoryname out of the publisch date */
char *fetchDirname(char *pubDate){
    char *dirname, *year, *month;
    int* pubDatePartsOffsets;
    
    pubDatePartsOffsets = replaceChars(pubDate, ' ', '\0');
    
    dirname = malloc(9*sizeof(char));
    year = malloc(5*sizeof(char));
    month = malloc(4*sizeof(char));
    year = strncpy(year, 1+pubDate+pubDatePartsOffsets[2], 5);
    month = strncpy(month, 1+pubDate+pubDatePartsOffsets[1], 4);
	sprintf(dirname, "%s-%s", year, month);
    
    return dirname;
}

/** Parse the dictionary into an rssitem struct */
struct rssitem* parseItem(be_dict *dictionary){
	struct rssitem *item;
	int i;

	item = malloc(sizeof(struct rssitem));

	for (i = 0; dictionary[i].val; ++i) {
		if(strcmp(dictionary[i].key,"title")==0){
			item->title=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"link")==0){
			item->link=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"description")==0){
			item->description=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"pubDate")==0){
			item->pubDate=dictionary[i].val->val.s;
		}else if(strcmp(dictionary[i].key,"guid")==0){
			item->guid=dictionary[i].val->val.s;
		}
	}
	
	return item;
}

/** The function from as3_t2. Reads the contents of the file to struct rsschannel *rss */
struct rsschannel* readFileToRSS(char* filename){
	FILE *fp;
	int size;
	
	// open the file
	fp = fopen(filename, "r");
	
	if (fp == NULL){
		printf("File %s doesn't exists!\n", filename);
		exit(0);
	}
	
	// seek the end of the file
	fseek(fp, 0, SEEK_END); 
	// get the current pointer in the file: THE END :)

	size = ftell(fp)/sizeof(char); 
	// set pointer back to the start of the file

	fseek(fp, 0, SEEK_SET); 
	
	// allocate a buffer to put the file in
	char buffer[size];
	// define some variables
	be_node* channel;
	struct rsschannel *rss;
	int i;
	
	// place the file in the buffer
	fgets(buffer, size, fp);

	// convert the buffercontents to a be_node
	channel = be_decode(buffer);
	// alloc memory for the RSS data structure
	rss = malloc(sizeof(struct rsschannel));

	// check the channel to be of type dictionary
	if (channel->type!=BE_DICT) {
		printf("Channel needs to be dictionary!"); 
		exit(0); 
	}

	// initialize item-values
	rss->itemCount = 0;
	rss->items = NULL;

	// walk through all attributes of the be_node and put the data in the RSS channel data structure
	for (i = 0; channel->val.d[i].val; ++i) {
		if(strcmp(channel->val.d[i].key, "title")==0){
			rss->title = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "link")==0){
			rss->link = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "description")==0){
			rss->description = channel->val.d[i].val->val.s;
		} else if(strcmp(channel->val.d[i].key, "language")==0){
			rss->language = channel->val.d[i].val->val.s;
		} else if(strncmp(channel->val.d[i].key, "item", 4)==0){
			rss->itemCount++;
			// realloc the items list
			rss->items = realloc(rss->items, rss->itemCount * sizeof(rssitem*));
			// parse the item and place it in the list of items
			rss->items[rss->itemCount - 1] = parseItem(channel->val.d[i].val->val.d);
		}
	}
	// close the file
	fclose(fp);
	
	return rss;
}

/** Create a file and add it to the right directory */
static void createFile(int index, char* name, char* dirname, char* data){
	struct inode_stat *file_stat, *dir_stat;
    struct inode *dir;
    
	file_stat = malloc(sizeof(struct inode_stat));
    
    // Check if directory exists and if so, find it
    dir = get_first_inode(get_root_inode());
    while(dir!=NULL && strcmp(get_inode_name(dir), dirname)!=0){
        dir = get_next_inode(dir);
    }
    
    if(dir==NULL){
        // Directory doesn't exist
        dir_stat = malloc(sizeof(struct inode_stat));
        dir_stat->mode = S_IFDIR | 0555;
        dir_stat->uid = 0;
        dir_stat->gid = 0;
        dir_stat->size = 0;
        dir_stat->dev = NO_DEV;
        dir = add_inode(get_root_inode(), dirname, NO_INDEX, dir_stat, 0,(cbdata_t) 0);
    }
    
	file_stat->mode = S_IFREG | 0444;
	file_stat->uid = 0;
	file_stat->gid = 0;
	file_stat->size = 0;
	file_stat->dev = NO_DEV;
    //printf("Creating file '%s' in '%s'\n", name, dirname);
	files[index] = add_inode(dir, name, NO_INDEX, file_stat, 0, (cbdata_t) data);
}

/** Hook called when vtreeFS is initializing */
static void my_init_hook(void){       
	char *content, *name, *dirname;
	int i, length;
	
	content = NULL;
	name = NULL;
	
	// Read the file, parse the file and save it to static struct rss
	rss = readFileToRSS(FILE_LOCATION);
	
	// Allocate memory for the files array storage
	files = malloc(rss->itemCount*sizeof(struct inode*));
	
	
	for (i = 0; i<rss->itemCount; i++) {
		// Calculate the number of bytes to allocate for the content of the file
		length = strlen(rss->items[i]->title) + 
		strlen(rss->items[i]->link) + 
		strlen(rss->items[i]->pubDate) + 
		strlen(rss->items[i]->guid) + 
		strlen(rss->items[i]->description) + 64;
		
		// Allocate space for the content and name of the file
		content = malloc(length*sizeof(char));
		name = malloc(24*sizeof(char));
		
		// Generate the content of the file
		sprintf(content, "title=%s\nlink=%s\npubDate=%s\nguid=%s\ndescription=%s", rss->items[i]->title, rss->items[i]->link, rss->items[i]->pubDate, rss->items[i]->guid, rss->items[i]->description);  
		// Generate the name of the file
		sprintf(name, "item-%d", i);
        
        dirname = fetchDirname(rss->items[i]->pubDate);
        
		// Create the file and add it to the root directory
		createFile(i, name, dirname, content);
	}
	
}

/** Hook called when you read a file */
static int my_read_hook(struct inode *inode, off_t offset, char **ptr, size_t *len, cbdata_t cbdata){
	// cbdata is actually the data stored in the file, so use that as content to return
	char *content = (char*)cbdata;
	// Calculate the length of the content
	int lenght = strlen(content);
	
	// Reallocate memory if needed
	data = realloc(data, sizeof(char)*lenght);
	
	strcpy(data, content);
	
	/* If the offset is beyond the end of the string, return EOF. */
	if (offset > strlen(data)) {
		*len = 0;

		return OK;
	}
	
	

	/* Otherwise, return a pointer into 'data'. If necessary, bound the
	* returned length to the length of the rest of the string. Note that
	* 'data' has to be static, because it will be used after this function
	* returns.
	*/
	*ptr = data + offset;

	if (*len > strlen(data) - offset)
	*len = strlen(data) - offset;

	return OK;
}

/* The table with callback hooks. */
struct fs_hooks my_hooks = {
	my_init_hook,
	NULL, /* cleanup_hook */
	NULL, /* lookup_hook */
	NULL, /* getdents_hook */
	my_read_hook,
	NULL, /* rdlink_hook */
	NULL  /* message_hook */
};

int main(void){
	struct inode_stat root_stat;
	
	data = NULL;

	/* Make the root directory */
	root_stat.mode = S_IFDIR | 0555;
	root_stat.uid = 0;
	root_stat.gid = 0;
	root_stat.size = 0;
	root_stat.dev = NO_DEV;

	/* Now start VTreeFS. Preallocate 10 inodes, which is more than we'll
	* need for this example. No indexed entries are used.
	*/
	start_vtreefs(&my_hooks, 10, &root_stat, 0);

	/* The call above never returns. This just keeps the compiler happy. */
	return 0;
}

